package helper.scxml.scxml2.zone.t2

import helper.base.*
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.addIfAbsent
import helper.base.LHMHelper.A2LHMExpand.addList
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.LHMHelper.A3LHM
import helper.base.LHMHelper.A3LHM.Companion.makeA3LHM
import helper.base.TupleHelper.GetTuple.getA2Tuple
import helper.base.ZoneConstraintHelper.Constraint
import helper.base.ZoneConstraintHelper.PointConstraint.Companion.makePointConstraint
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.debug.DebugRes.d_aLocationEnterConstraintLHM
import helper.base.debug.DebugRes.d_getCalString
import helper.base.debug.DebugRes.d_randomRun
import helper.base.debug.DebugRes.d_randomRun_runResult
import helper.base.debug.DebugRes.d_repeatRandomRun
import helper.base.debug.DebugRes.d_t19
import helper.base.debug.DebugRes.d_t2t2_while
import helper.base.debug.DebugRes.d_t_pT1Calculate_path
import helper.base.math.GetDecayingEpsilonHelper.selectStrategy
import helper.base.math.MathHelper.modifiedMinus
import helper.scxml.scxml2.EnvHelper.RunResult
import helper.scxml.scxml2.EnvHelper.RunResult.Companion.makeRunResult
import helper.scxml.scxml2.EnvHelper.StateEventDataUnit.Companion.makeStateEventDataUnit
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.ConfigHelper.TimeEventChoiceArrConfigWeightedPathLHM
import helper.scxml.scxml2.zone.t2.ConfigHelper.TimeEventChoiceArrConfigWeightedPathLHM.Companion.makeTimeEventChoiceArrConfigWeightedPathLHM
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ConstraintGetterFunArg
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ConstraintGetterFunArg.Companion.makeConstraintGetterFunArg
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.DoubleDoubleFunTuple.Companion.makeDoubleDoubleFunTuple
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.StringActionListLHM.Companion.makeStringActionListLHM
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ZoneEnv.Companion.tClock
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEventMinTimePQ
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit

typealias StrStrBoolFun = (String) -> (String) -> Boolean
typealias ConstraintGetterFun = (ConstraintGetterFunArg) -> Constraint
typealias NoteRunResultFun = (String, Config, TimedEvent, RunResult) -> Unit

object ZoneEnvHelper {
    val equal_name_StrStrBoolFun: StrStrBoolFun = { name ->
        {
            it == name
        }
    }

    val starts_with_name_StrStrBoolFun: StrStrBoolFun = { name ->
        {
            it.startsWith(name)
        }
    }

    class ConstraintGetterArg(
        val oldStates: ArrayList<String>,
        val nowConfig: Config,
        val timeEventChoiceArr: List<TimedEvent>,
        val newStates: ArrayList<String>,
    ) {
        companion object {
            fun makeConstraintGetterArg(
                oldStates: ArrayList<String>,
                nowConfig: Config,
                timeEventChoiceArr: List<TimedEvent>,
                newStates: ArrayList<String>,
            ): ConstraintGetterArg {
                return ConstraintGetterArg(oldStates, nowConfig, timeEventChoiceArr, newStates)
            }
        }
    }

    class ConstraintGetterFunArg(
        val oldStates: ArrayList<String>,
        val oldConstraint: Constraint,
        val timeEventChoiceArr: List<TimedEvent>,
        val newStates: ArrayList<String>,
    ) {
        companion object {
            fun makeConstraintGetterFunArg(
                oldStates: ArrayList<String>,
                oldC: Constraint,
                timeEventChoiceArr: List<TimedEvent>,
                newStates: ArrayList<String>,
            ): ConstraintGetterFunArg {
                require(timeEventChoiceArr.isNotEmpty())
                val firstTime = timeEventChoiceArr[0].time
                require(timeEventChoiceArr.all { it.time == firstTime })
                return ConstraintGetterFunArg(oldStates, oldC, timeEventChoiceArr, newStates)
            }

            fun makeConstraintGetterFunArg(
                constraintGetterArg: ConstraintGetterArg,
                oldZC: Constraint,
            ): ConstraintGetterFunArg {
                return makeConstraintGetterFunArg(
                    constraintGetterArg.oldStates,
                    oldZC,
                    constraintGetterArg.timeEventChoiceArr,
                    constraintGetterArg.newStates,
                )
            }
        }

        // 定义了四个组件函数，允许通过解构声明语法访问类属性。
        operator fun component1() = oldStates
        operator fun component2() = oldConstraint
        operator fun component3() = timeEventChoiceArr
        operator fun component4() = newStates
    }

    data class ConstraintGetter(
        val name: String,
        val strStrBoolFun: StrStrBoolFun,
        val constraintGetterFun: ConstraintGetterFun,
    ) {
        companion object {
            fun makeConstraintGetter(
                name: String,
                getStringBooleanFunEnum: StrStrBoolFun,
                constraintGetterFun: ConstraintGetterFun,
            ): ConstraintGetter {
                return ConstraintGetter(
                    name,
                    getStringBooleanFunEnum,
                    constraintGetterFun,
                )
            }
        }

        fun getOldConstraint(fromConfig: Config): Constraint {
            return fromConfig.byFunFindC(strStrBoolFun(name))!!
        }

        fun getNewConstraint(constraintGetterArg: ConstraintGetterArg): Constraint {
            return constraintGetterFun(
                makeConstraintGetterFunArg(
                    constraintGetterArg,
                    getOldConstraint(
                        constraintGetterArg.nowConfig,
                    ),
                ),
            )
        }
    }

    class Edge(
        val locationStart: String,
        val locationEnd: String,
        val edgeName: String = locationStart + locationEnd,
    ) {
        companion object {
            fun makeEdge(
                locationStart: String,
                locationEnd: String,
                edgeName: String? = null,
            ): Edge {
                return if (edgeName == null) {
                    Edge(locationStart, locationEnd)
                } else {
                    Edge(locationStart, locationEnd, edgeName)
                }
            }
        }
    }

    interface IAssignFunTuple {
        fun getNewD(oldConfig: Config): Double
    }

    class VoidDoubleFunTuple(
        val theFun: VoidDoubleFun,
    ) : IAssignFunTuple {
        companion object {
            fun makeVoidDoubleFunTuple(theFun: VoidDoubleFun): VoidDoubleFunTuple {
                return VoidDoubleFunTuple(theFun)
            }
        }

        override fun getNewD(oldConfig: Config): Double {
            return theFun()
        }
    }

    class DoubleDoubleFunTuple(
        val varName: String,
        val theFun: DoubleDoubleFun,
    ) : IAssignFunTuple {
        companion object {
            fun makeDoubleDoubleFunTuple(varName: String, theFun: DoubleDoubleFun): DoubleDoubleFunTuple {
                return DoubleDoubleFunTuple(varName, theFun)
            }
        }

        override fun getNewD(oldConfig: Config): Double {
            return theFun.invoke(oldConfig.varConstraintLHM[varName]!!.getPointD())
        }
    }

    class DoubleListDoubleFunTuple(
        val varNameList: ArrayList<String>,
        val theFun: DoubleListDoubleFun,
    ) : IAssignFunTuple {
        companion object {
            fun makeDoubleListDoubleFunTuple(
                varNameList: ArrayList<String>,
                theFun: DoubleListDoubleFun,
            ): DoubleListDoubleFunTuple {
                return DoubleListDoubleFunTuple(varNameList, theFun)
            }
        }

        override fun getNewD(oldConfig: Config): Double {
            val values = ArrayList<Double>()
            varNameList.forEach { varName ->
                values.add(oldConfig.varConstraintLHM[varName]!!.getPointD())
            }
            return theFun.invoke(values)
        }
    }

    interface IAction {
        fun doAction(oldConfig: Config, newConfig: Config)
    }

    class SendAction(
        val timedEventList: List<TimedEvent>,
    ) : IAction {
        companion object {
            fun makeSendAction(timedEventList: List<TimedEvent>) = SendAction(timedEventList)
            fun makeSendAction(timedEvent: TimedEvent) = makeSendAction(listOf(timedEvent))
        }

        override fun doAction(oldConfig: Config, newConfig: Config) {
            timedEventList.forEach {
                newConfig.heap.heap.add(it)
            }
        }
    }

    class AssignAction(
        val varName: String,
        val assignFunTuple: IAssignFunTuple,
    ) : IAction {
        companion object {
            fun makeAssignAction(
                varName: String,
                assignFunTuple: IAssignFunTuple,
            ) = AssignAction(varName, assignFunTuple)

            fun makeAssignAction(
                varName: String,
                doubleDoubleFun: DoubleDoubleFun,
            ) = makeAssignAction(varName, makeDoubleDoubleFunTuple(varName, doubleDoubleFun))
        }

        override fun doAction(oldConfig: Config, newConfig: Config) {
            newConfig.varConstraintLHM[varName] = makePointConstraint(
                varName,
                assignFunTuple.getNewD(oldConfig),
            )
        }
    }

    class StringActionListLHM : A2LHM<String, ArrayList<IAction>>() {
        companion object {
            fun makeStringActionListLHM() = StringActionListLHM()
        }

        fun doActions(
            s: String,
            oldConfig: Config,
            newConfig: Config,
        ) {
            this[s]?.forEach {
                it.doAction(oldConfig, newConfig)
            }
        }
    }

    abstract class ZoneEnv {
        companion object {
            val tClock = "t"
        }

        open val clocks: List<String> = listOf(tClock)

        abstract val allVars: List<String>

        open val timeNeedEnd: Int = Int.MAX_VALUE

        open val aT1LsStringFun: ArrayList<String>.() -> String by lazy {
            {
                this.joinToString(separator = ",")
            }
        }

        open val aT1CsStringFun: ArrayList<Constraint>.() -> String by lazy {
            {
                this.joinToString(",") {
                    it.toStr0()
                }
            }
        }

        open fun isOnFinalLocation(nowZS: Config): Boolean = false

        abstract fun isEnd(nowZS: Config, nextMinEventTime: Int = 0): Boolean

        fun isEnd0(nowZS: Config) = isEnd(nowZS)

        //边
        open val aStateEventStateLHM: A3LHM<String, String, String> = makeA3LHM()

        //最大停留时间
        abstract val stateStayMaxLHM: A2LHM<String, Int>

        open val stateVarRateA3LHM: A3LHM<String, String, Int> = makeA3LHM()
        open val stateEnterActionLHM: StringActionListLHM = makeStringActionListLHM()
        open val stateExitActionLHM: StringActionListLHM = makeStringActionListLHM()
        open val eventActionLHM: StringActionListLHM = makeStringActionListLHM()

        //遍历，可优化成以使用固定event开头的lhm的方式
        private val aEventStartStateListLHM: A2LHM<String, ArrayList<String>> = makeA2LHM()
        fun getStartStateOfEvent(event: String): ArrayList<String> {
            if (aEventStartStateListLHM.isEmpty()) {
                aStateEventStateLHM.touch { s, s2, s3 ->
                    aEventStartStateListLHM.getOrPut(s2) {
                        arrayListOf()
                    }.add(s)
                }
            }
            return aEventStartStateListLHM[event]!!
        }

        abstract fun getInitialConfig(debuggerList: DebuggerList): Config

        //运行到特定State时，将运行数据记录到RunResult中
        abstract val aStateNoteRunResultFunLHM: A2LHM<String, NoteRunResultFun>

        //运行到end时，将数据记录到RunResult中
        protected fun noteRunResultWhenEnd(vars: List<String>, nowConfig: Config, runResult: RunResult) {
            vars.forEach {
                runResult.endData[it] = nowConfig.byNameFindC(it).getPointD().toString()
            }
        }

        open fun doWhenEnd(nowConfig: Config, debuggerList: DebuggerList) {}

        open fun noteRunResultWhenEnd(nowConfig: Config, runResult: RunResult, debuggerList: DebuggerList) {
            noteRunResultWhenEnd(allVars, nowConfig, runResult)
        }

        fun noteRunResultWhenStateEvent(config: Config, state: String, timedEvent: TimedEvent, runResult: RunResult) {
            aStateNoteRunResultFunLHM[state]?.let {
                it(state, config, timedEvent, runResult)
            }
        }

        open fun isBadEnd(nowConfig: Config) = false

        open fun doIfBadEnd(nowConfig: Config, debuggerList: DebuggerList) {}

        open fun updateConstraint(
            exitStateList: ArrayList<String>,
            oldConfig: Config,
            newConfig: Config,
            timedEventList: List<TimedEvent>,
        ) {
        }

        private fun getNextConfigTuple(
            oldConfig: Config,
            //同一时刻的所有timeEventChoice
            timedEventList: List<TimedEvent>,
            runResult: RunResult? = null,
            debuggerList: DebuggerList = makeDebuggerList(),
        ): A2Tuple<Config, Boolean> {
            debuggerList.run {
                d(
                    d_t19,
                ) {
                    println("fun getNextZoneStateTuple")
                    println("oldZS=${oldConfig.toT1Str()}}")
                    println("timeEventChoiceList=${timedEventList}")
                }
            }

            // 保证 timeEventChoiceList 非空
            require(timedEventList.isNotEmpty()) {
                println("timeEventChoiceList.isEmpty()")
                println("oldZS=${oldConfig.toT1Str()}")
            }
            // 保证 timeEventChoiceLis 里的 time 都相等
            timedEventList[0].time.let { firstTime ->
                require(timedEventList.all {
                    it.time == firstTime
                })
            }
            // 深度拷贝 oldConfig
            val newConfig = oldConfig.deepClone()
            var ifAnyChoiceSuccess = false
            val needToRemove2 = arrayListOf<String>()
            timedEventList.forEach { timedEvent ->
                debuggerList.run {
                    d(
                        d_t19,
                    ) {
                        println("timedEvent=${timedEvent}")
                    }
                }
                val needToRemove = arrayListOf<String>()
                val needToAdd = arrayListOf<String>()
                debuggerList.run {
                    d(
                        d_t19,
                    ) {
                        println("newConfig.states=${newConfig.states}")
                    }
                }
                newConfig.states.forEach { startL ->
                    debuggerList.run {
                        d(
                            d_t19,
                        ) {
                            println("startL=${startL}")
                        }
                    }
                    aStateEventStateLHM[startL, timedEvent.event]?.let { endL ->
                        debuggerList.run {
                            d(
                                d_aLocationEnterConstraintLHM,
                            ) {
                                println("startL=${startL},event=${timedEvent.event},endL=${endL}")
                            }
                        }
                        // 判断是否满足进入目标状态的条件
                        val b = aStateEnterConstraintLHM[endL]?.let {
                            aStateEnterConstraintLHM[endL]!!(newConfig)
                        } ?: true
                        debuggerList.run {
                            d(
                                d_t19,
                            ) {
                                println("b=${b}")
                            }
                        }
                        if (b) {
                            needToRemove.add(startL)
                            needToRemove2.add(startL)
                            needToAdd.add(endL)
                            ifAnyChoiceSuccess = true
                        }
                    }
                }
                needToRemove.forEach {
                    newConfig.states.remove(it)
                }
                needToAdd.forEach {
                    newConfig.states.add(it)
                }
                needToRemove.forEach {
                    if (runResult != null) {
                        noteRunResultWhenStateEvent(
                            oldConfig,
                            it,
                            timedEvent,
                            runResult,
                        )
                    }
                }
            }
            debuggerList.run {
                d(
                    d_t19,
                ) {
                    println("ifAnyChoiceSuccess=${ifAnyChoiceSuccess}")
                }
            }
            if (ifAnyChoiceSuccess) {
                updateConstraint(
                    needToRemove2,
                    oldConfig,
                    newConfig,
                    timedEventList,
                )
            }
            val a2Tuple = getA2Tuple(newConfig, ifAnyChoiceSuccess)
            debuggerList.run {
                d(
                    d_t19,
                ) {
                    println("newZS=${newConfig.toT1Str()}}")
                    println("end fun getNextZoneStateTuple")
                }
            }
            return a2Tuple
        }

        private fun sendEventsToHeap(
            timedEventList: List<TimedEvent>,
            heap: TimedEventMinTimePQ,
            ifCanSendEventHaveSent: Boolean,
        ) {
            timedEventList.forEach {
                if (ifCanSendEventHaveSent || !heap.containsEvent(it.event)) {
                    heap.heap.add(it)
                }
            }
        }

        fun getPollTimeEventChoiceList(heap: TimedEventMinTimePQ): ArrayList<TimedEvent> {
            return heap.pollAllMinTime()
        }

        fun randomRun(
            strategy: ISNodeUnit,
            runResult: RunResult = makeRunResult(),
            aLsStringFun: ArrayList<String>.() -> String = aT1LsStringFun,
            aCsStringFun: ArrayList<Constraint>.() -> String = aT1CsStringFun,
            debuggerList: DebuggerList = makeDebuggerList(),
        ): RunResult {
            debuggerList.run {
                d(
                    d_aLocationEnterConstraintLHM,
                ) {
                    println("aLocationEnterConstraintLHM=${aStateEnterConstraintLHM}")
                }
            }

            var nowG: Config = getInitialConfig(debuggerList)

            debuggerList.run {
                d(
                    d_randomRun,
                ) {
                    println("-".repeat(80))
                    println("d_randomRun:")
                }
            }

            fun printNowZS() {
                debuggerList.run {
                    d(
                        d_randomRun,
                    ) {
                        println(
                            "nowG=${
                                nowG.toT0Str(
                                    aStatesStringFun = aLsStringFun,
                                    aCsStringFun = aCsStringFun,
                                )
                            }"
                        )
                    }
                }
            }

            printNowZS()
            var isEnd = isEnd(nowG)

            while (!isEnd) {
                val aChoiceList = strategy.getMsTimeEventChoiceList(nowG)
                debuggerList.run {
                    d(d_randomRun) {
                        println("aChoiceList=${aChoiceList}")
                    }
                }
                sendEventsToHeap(
                    aChoiceList,
                    nowG.heap,
                    strategy.ifCanSendEventHaveSent,
                )

                var ifAnyChoiceSuccess = false
                while (!isEnd && !ifAnyChoiceSuccess) {
                    debuggerList.run {
                        d(d_t19) {
                            println("while.")
                            printNowZS()
                        }
                    }
                    val aPollTimeEventChoiceList = nowG.heap.pollAllMinTime()
                    debuggerList.run {
                        d(
                            d_randomRun,
                        ) {
                            println("pollTimeEventChoiceList=${aPollTimeEventChoiceList}")
                        }
                    }
                    require(aPollTimeEventChoiceList.size > 0) {
                        println("nowZS=${nowG.toT1Str()}")
                    }
                    val aTime = aPollTimeEventChoiceList[0].time
                    isEnd = isEnd(nowG, aTime)
                    debuggerList.run {
                        d(
                            d_t19,
                        ) {
                            println("isEnd=${isEnd}")
                        }
                    }
                    if (!isEnd) {
                        val aNextZoneStateTuple = getNextConfigTuple(
                            oldConfig = nowG,
                            timedEventList = aPollTimeEventChoiceList,
                            runResult = runResult,
                            debuggerList = debuggerList,
                        )
                        nowG = aNextZoneStateTuple.first
                        printNowZS()
                        ifAnyChoiceSuccess = aNextZoneStateTuple.second
                        isEnd = isEnd(nowG)
                    }
                }
            }
            doWhenEnd(
                nowG,
                debuggerList,
            )
            noteRunResultWhenEnd(
                nowG,
                runResult,
                debuggerList,
            )
            printNowZS()
            debuggerList.run {
                d(
                    d_randomRun,
                ) {
                    println(":d_randomRun")
                }
            }
            return runResult
        }

        fun repeatRandomRun(
            aSNU: ISNodeUnit,
            repeatTimes: Int = 1,
            episode: Double = 1.0,
            aUniformSNU: ISNodeUnit = aSNU,
            runResultList: ArrayList<RunResult> = ArrayList(),
            aLsStringFun: ArrayList<String>.() -> String = aT1LsStringFun,
            aCsStringFun: ArrayList<Constraint>.() -> String = aT1CsStringFun,
            debuggerList: DebuggerList = makeDebuggerList(),
        ): ArrayList<RunResult> {
            require(repeatTimes > 0)
            val choiceSNU = selectStrategy(
                aSNU,
                episode,
                aUniformSNU,
            )
            repeat(repeatTimes) {
                debuggerList.run {
                    d(
                        d_repeatRandomRun,
                    ) {
                        println("repeatTimes=${it}")
                    }
                }
                randomRun(
                    strategy = choiceSNU,
                    aLsStringFun = aLsStringFun,
                    aCsStringFun = aCsStringFun,
                    debuggerList = debuggerList,
                ).let {
                    runResultList.add(
                        it,
                    )
                }
            }
            debuggerList.run {
                d(
                    d_randomRun_runResult,
                ) {
                    runResultList.forEach {
                        println("runResult=${it.toL2String()}")
                    }
                }
            }
            return runResultList
        }

        fun aT0Calculate(
            aSNU: ISNodeUnit,
            repeatTimes: Int = 1000,
            aLsStringFun: ArrayList<String>.() -> String = aT1LsStringFun,
            aCsStringFun: ArrayList<Constraint>.() -> String = aT1CsStringFun,
            debuggerList: DebuggerList = makeDebuggerList(),
        ): A2LHM<String, Double> {
            val res = makeA2LHM<String, Double>()
            val runResults = repeatRandomRun(
                aSNU = aSNU,
                repeatTimes = repeatTimes,
                aLsStringFun = aLsStringFun,
                aCsStringFun = aCsStringFun,
                debuggerList = debuggerList,
            )
            allVars.forEach { aVar ->
                val totalValue = runResults.sumOf {
                    it.endData[aVar]!!.toDouble()
                }
                val expectedValue = totalValue / repeatTimes.toDouble()
                res.add(aVar, expectedValue)
            }
            return res
        }

        private fun pT1Calculate(
            //策略
            aSNU: ISNodeUnit,
            //路径簇
            lhm: TimeEventChoiceArrConfigWeightedPathLHM = makeTimeEventChoiceArrConfigWeightedPathLHM(),
            debuggerList: DebuggerList = makeDebuggerList(),
            //路径
            path: ArrayList<TimedEventListConfigTuple> = arrayListOf(
                getA2Tuple(
                    arrayListOf(),
                    getInitialConfig(debuggerList),
                ),
            ),
        ): A2LHM<String, Double>? {
            debuggerList.run {
                d(
                    d_t2t2_while,
                ) {
                    println("${this.javaClass.simpleName},pT1Calculate")
                    println("path.size=${path.size}")
                    path.forEach {
                        println("\t${it.first}")
                        println("\t${it.second}")
                    }
                }
            }
            var nowTuple = path.last()
            var (_, config) = nowTuple
            debuggerList.run {
                if (path.size > 1) {
                    return@run
                }
                d(
                    d_t_pT1Calculate_path,
                ) {
                    println("nowConfig=${config}")
                }
            }
            val aChoiceWeightLHM = aSNU.getMsTimeEventChoiceWeightLHM(
                config,
            )
            val sum = aChoiceWeightLHM.values.sum()
            aChoiceWeightLHM.forEach { (aChoiceList: List<TimedEvent>, weight: Double) ->
                val oldTuple = nowTuple
                val oldZS = config.deepClone()

                sendEventsToHeap(
                    aChoiceList,
                    config.heap,
                    aSNU.ifCanSendEventHaveSent,
                )

                val allPollTimeEventChoiceList = ArrayList<TimedEvent>()
                var isEnd = false
                var ifAnyChoiceSuccess = false
                while (!isEnd && !ifAnyChoiceSuccess) {
                    val aPollTimeEventChoiceList = config.heap.pollAllMinTime()
                    aPollTimeEventChoiceList.forEach {
                        allPollTimeEventChoiceList.add(it)
                    }
                    require(aPollTimeEventChoiceList.size > 0) {
                        println("nowConfig=${config.toT1Str()}")
                    }
                    val aTime = aPollTimeEventChoiceList[0].time
                    isEnd = isEnd(config, aTime)
                    if (!isEnd) {
                        val aNextZoneStateTuple = getNextConfigTuple(
                            oldConfig = config,
                            timedEventList = aPollTimeEventChoiceList,
                            debuggerList = debuggerList,
                        )
                        config = aNextZoneStateTuple.first
                        ifAnyChoiceSuccess = aNextZoneStateTuple.second
                        isEnd = isEnd(config)
                        debuggerList.run {
                            d(
                                d_t19,
                            ) {
                                println("in pT1Calculate after getNext, nowConfig=${config.toT1Str()}")
                            }
                        }
                    }
                }
                if (isEnd) {
                    if (isBadEnd(config)) {
                        doIfBadEnd(
                            config,
                            debuggerList,
                        )
                    } else {
                        doWhenEnd(
                            config,
                            debuggerList,
                        )
                    }
                }
                nowTuple = getA2Tuple(allPollTimeEventChoiceList, config)
                val theWeightOfSum = weight / sum
                lhm.addList(
                    oldTuple,
                    getA2Tuple(
                        nowTuple,
                        theWeightOfSum,
                    ),
                )
                path.add(nowTuple)
                debuggerList.run {
                    d(
                        d_t_pT1Calculate_path,
                    ) {
                        val tab = "\t".repeat(path.size.modifiedMinus(1))
                        println(buildString {
                            append("${tab}timeEventChoice=${allPollTimeEventChoiceList},")
                            append("nowConfig=${config},")
                            append("theWeightOfSum=${theWeightOfSum}")
                        })
                    }
                }
                if (!isEnd) {
                    pT1Calculate(
                        aSNU = aSNU,
                        lhm = lhm,
                        path = path,
                        debuggerList = debuggerList,
                    )
                }
                path.removeLast()
                nowTuple = oldTuple
                config = oldZS
            }
            return if (path.size <= 1) {
                debuggerList.run {
                    d(
                        d_getCalString,
                    ) {
                        println("lhm.getCalString()=${lhm.getCalString()}")
                    }
                }
                lhm.cal(
                    debuggerList = debuggerList,
                )
            } else {
                null
            }
        }

        open val aStringEToDoubleLHM by lazy {
            A2LHM<String, (TimedEventListConfigTuple) -> Double>().also { lhm ->
                allVars.forEach { v ->
                    lhm.add(v) {
                        it.second.byNameFindC(v).getPointD()
                    }
                }
            }
        }

        fun aT1Calculate(aSNU: ISNodeUnit, debuggerList: DebuggerList = makeDebuggerList()): A2LHM<String, Double> {
            return pT1Calculate(
                aSNU = aSNU,
                lhm = makeTimeEventChoiceArrConfigWeightedPathLHM(
                    aStringEToDoubleLHM = aStringEToDoubleLHM,
                ),
                debuggerList = debuggerList,
            )!!
        }

        open val aStateEnterConstraintLHM: A2LHM<String, (Config) -> Boolean> = makeA2LHM()
    }

    interface IStrategyEx<E : ISNodeUnit> {
        fun makeNewStrategy(): E
        fun makeFixedStrategy(): E
    }

    val tNoteRunResultFun: NoteRunResultFun =
        { state: String, nowConfig: Config, timedEvent: TimedEvent, runResult: RunResult ->
            runResult.us.add(
                makeStateEventDataUnit(
                    state,
                    timedEvent.event,
                    makeA2LHM<String, String>().also {
                        it.addIfAbsent("t", "${nowConfig.byNameFindC(tClock).getPointD() + timedEvent.time}")
                    },
                )
            )
        }

    val txNoteRunResultFun: NoteRunResultFun =
        { state: String, nowConfig: Config, timedEvent: TimedEvent, runResult: RunResult ->
            runResult.us.add(
                makeStateEventDataUnit(
                    state,
                    timedEvent.event,
                    makeA2LHM<String, String>().also {
                        it.addIfAbsent("t", "${nowConfig.byNameFindC(tClock).getPointD() + timedEvent.time}")
                        it.addIfAbsent("x", "${timedEvent.time}")
                    },
                )
            )
        }
}